package com.open.pay.common.client;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.open.framework.enums.ErrorEnum;
import com.open.framework.exception.Assert;
import com.open.framework.exception.ExceptionFactory;
import com.open.pay.common.util.Tuple2;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Headers;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author zhang kaichao
 */
@Slf4j
@Component
public class RemoteCallClient {

    private static RestTemplate restTemplate;
    private static ObjectMapper objectMapper;
    private static OkHttpClient okHttpClient;
    private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");

    public static <T> T sendHttpPost(String url, Object body, MultiValueMap<String, String> headers, Class<T> type) {
        return sendHttpPost(url, body, headers, type, null);
    }

    public static <T> T sendHttpPost(String url, Object body, MultiValueMap<String, String> headers, Class<T> type, Class eType) {
        return send(url, HttpMethod.POST, body, null, headers, type, eType);
    }

    public static <T> T sendHttpGet(String url, Map<String, Object> params, MultiValueMap<String, String> headers, Class<T> type) {
        return sendHttpGet(url, params, headers, type, null);
    }

    public static <T> T sendHttpGet(String url, Map<String, Object> params, MultiValueMap<String, String> headers, Class<T> type, Class eType) {
        return send(url, HttpMethod.GET, null, params, headers, type, eType);
    }

    private static <T> T send(String url, HttpMethod httpMethod, Object body, Map<String, Object> params, MultiValueMap<String, String> headers, Class<T> type, Class eType) {
        Assert.notNull(url, "'url'不能为空");
        Assert.notNull(type, "'type'不能为空");
        Assert.isFalse(url.contains("?") && params != null, "'url' 包含 '?' 和 'params' 不为空，两种情况不能同时存在");
        if (params != null) {
            String queryString = params.entrySet().stream().map(p -> p.getKey() + "=" + p.getValue()).collect(Collectors.joining("&"));
            if (queryString.length() > 0) {
                url += ("?" + queryString);
            }
        }
        HttpEntity<?> requestEntity = new HttpEntity<>(body, headers);
        try {
            log.info("RemoteCallClient.send{}[{}] request at {}", httpMethod.name(), url, objectMapper.writeValueAsString(body));
        } catch (JsonProcessingException e) {
            log.warn("Logging request occur exception:{}", e.getMessage());
        }

        String responseStr;
        try {
            ResponseEntity<String> responseEntity = restTemplate.exchange(url, httpMethod, requestEntity, String.class);
            responseStr = responseEntity.getBody();
            log.info("RemoteCallClient.send{}[{}] response code at {}, body at {}", httpMethod.name(), url, responseEntity.getStatusCodeValue(), responseStr);
        } catch (Exception e) {
            log.error("RemoteCallClient.send{}[{}] exchange occur exception at {}", httpMethod.name(), url, e.getMessage());
            e.printStackTrace();
            throw ExceptionFactory.of(ErrorEnum.DEFAULT);
        }
        try {
            if (eType == null) {
                return objectMapper.readValue(responseStr, type);
            }
            return objectMapper.readValue(responseStr, objectMapper.getTypeFactory().constructParametricType(type, eType));
        } catch (IOException e) {
            log.error("RemoteCallClient.send{}[{}] response readValue exception at {}", httpMethod.name(), url, e.getMessage());
            e.printStackTrace();
            throw ExceptionFactory.of(ErrorEnum.DEFAULT);
        }
    }


    private String request(final String domain,String urlSuffix, String data,String uuid, boolean autoReport,boolean useCert,String mchId,InputStream certStream) throws Exception {
        Tuple2<SSLContext, X509TrustManager> tuple = buildSSL(useCert, mchId, certStream);
        OkHttpClient newClient = okHttpClient.newBuilder().sslSocketFactory(tuple.getF0().getSocketFactory(), tuple.getF1())
                .build();
        String url = "https://" + domain + urlSuffix;
        RequestBody requestBody = RequestBody.create(JSON, data);

        Request.Builder builder = new Request.Builder()
                .post(requestBody)
                .url(url);
        doHeaders(builder);

        Request request = builder.build();
        Response response = newClient.newCall(request).execute();

        if (response.isSuccessful()) {

        } else {

        }


    }

    private Request.Builder getBuilder(String baseUrl, String url) {
        return new Request.Builder().url(String.format("%s%s", baseUrl, url));
    }


    private void doHeaders(Request.Builder builder) {
        Headers.Builder headBuilder = new Headers.Builder();
        headBuilder.add("","");
       builder.headers(headBuilder.build());
    }


    private Tuple2<SSLContext,X509TrustManager> buildSSL(boolean useCert,String mchId,InputStream certStream){
        X509TrustManager trustManager = null;
        SSLContext sslContext = null;
        try {
            trustManager = new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                }
                @Override
                public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                }
                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }
            };
            sslContext = SSLContext.getInstance("TLS");
            if(useCert){
                // 证书
                char[] password = mchId.toCharArray();
                KeyStore ks = KeyStore.getInstance("PKCS12");
                ks.load(certStream, password);

                // 实例化密钥库 & 初始化密钥工厂
                KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
                kmf.init(ks, password);
                sslContext.init(kmf.getKeyManagers(), null, new SecureRandom());
            }else{
                sslContext.init(null, new TrustManager[]{trustManager}, new SecureRandom());
            }
        } catch (Exception e) {
            log.error("restTemplate init fail:", e);
        }
        return Tuple2.of(sslContext,trustManager);
    }

    @Autowired
    public void setResultTemplate(RestTemplate restTemplate) {
        RemoteCallClient.restTemplate = restTemplate;
    }

    @Autowired
    public void setObjectMapper(ObjectMapper objectMapper) {
        RemoteCallClient.objectMapper = objectMapper;
    }

    @Autowired
    public void setOkHttpClient(OkHttpClient okHttpClient) {
        RemoteCallClient.okHttpClient = okHttpClient;
    }




}
